#include "helper.h"
#include "common.h"

#include <time.h>
#include <unistd.h>
#include <fstream>
#include <malloc.h>

// 生成随机数
long random(long max)
{
    // 获取开始执行时间
    struct timeval tv;
    long t = 0;
    gettimeofday(&tv, NULL);
    t = tv.tv_sec * 1000000 + tv.tv_usec;
    srand(t);

    long res = rand() % max;
    return res;
}

// 生成uuid
std::string uuid()
{
    const char *v = "0123456789abcdef";
    const bool dash[] = {0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0};
    std::string res;
    for (bool i : dash)
    {
        if (i)
        {
            res += "-";
        }
        res += v[random(16)];
        res += v[random(16)];
    }

    return res;
}

// 字符串替换
std::string str_replace(std::string subject, std::string source, std::string replace)
{
    std::string::size_type pos;
    std::vector<std::string> result;
    subject += source; //扩展字符串以方便操作
    int size = subject.size();
    int count = 0;
    std::string res = "";

    for (int i = 0; i < size; i++)
    {
        pos = subject.find(source, i);
        if (pos < (uint)size)
        {
            std::string s = subject.substr(i, pos - i);
            result.push_back(s);
            i = pos + source.size() - 1;
            count++;
        }
    }

    for (auto r : result)
    {
        count--;
        if (count == 0)
        {
            res = res + r;
        }
        else
        {
            res = res + r + replace;
        }
    }
    result.clear();
    std::vector<std::string>().swap(result);
    malloc_trim(0);

    return res;
}

// 获取 _SERVER 全局参数
std::string server_param(std::string name)
{
    char *nc = const_cast<char *>(name.c_str());
    zval *carrier = nullptr;
    zval *param;

    zend_bool jit_initialization = PG(auto_globals_jit);

    if (jit_initialization)
    {
        zend_string *server_str = zend_string_init("_SERVER", sizeof("_SERVER") - 1, 0);
        zend_is_auto_global(server_str);
        zend_string_release(server_str);
    }
    carrier = zend_hash_str_find(&EG(symbol_table), ZEND_STRL("_SERVER"));
    param = zend_hash_str_find(Z_ARRVAL_P(carrier), nc, name.length());

    std::string res = param == nullptr ? "" : Z_STRVAL_P(param);

    return res;
}

// 切割字符串
std::vector<std::string> split(std::string str, std::string pattern)
{
    std::string::size_type pos;
    std::vector<std::string> result;
    str += pattern; //扩展字符串以方便操作
    int size = str.size();

    for (int i = 0; i < size; i++)
    {
        pos = str.find(pattern, i);
        if (pos < (uint)size)
        {
            std::string s = str.substr(i, pos - i);
            result.push_back(s);
            i = pos + pattern.size() - 1;
        }
    }

    return result;
}

// 输出日志
extern char *logdir;
void logger(std::string msg)
{
    if (logdir == nullptr)
    {
        return;
    }
    std::string path = logdir;

    // 获取开始执行时间
    time_t time_obj;
    time(&time_obj);
    char time[64];
    strftime(time, sizeof(time), "[%Y-%m-%d %H:%M:%S] ", localtime(&time_obj));

    int pid = getpid();

    std::ofstream ofs;

    std::ifstream in(path.c_str());
    in.seekg(0, std::ios::end);
    size_t size = in.tellg();
    in.close();
    if ((int)size > 104857600)
    {
        ofs.open(path, std::ios::trunc);
    }
    else
    {
        ofs.open(path, std::ios::app);
    }

    ofs << time << "[" << pid << "] " << msg << std::endl;
    ofs.close();
}

// 判断是否以指定字符串开头
bool starts_with(const char *pre, const char *str)
{
    size_t len_pre = strlen(pre),
           len_str = strlen(str);
    return len_str < len_pre ? false : memcmp(pre, str, len_pre) == 0;
}

// 读取对象属性
zval *read_obj_property(zval *obj, const char *property, std::string obj_class)
{
    if (Z_TYPE_P(obj) == IS_OBJECT)
    {
        zend_object *object = obj->value.obj;
        zend_class_entry *ce;

        ce = object->ce;

        zval *res = zend_read_property(ce, obj, property, strlen(property), 0, nullptr);
        if (Z_TYPE_P(res) == IS_OBJECT && ZSTR_VAL((res->value.obj)->ce->name) == obj_class)
        {
            return res;
        }
    }
    return nullptr;
}

// 读取对象属性
zval *read_property(zval *obj, const char *property)
{
    if (Z_TYPE_P(obj) == IS_OBJECT)
    {
        zend_object *object = obj->value.obj;
        zend_class_entry *ce;

        ce = object->ce;

        return zend_read_property(ce, obj, property, strlen(property), 0, nullptr);
    }
    return nullptr;
}

std::string str_replace_all(std::string source, std::string old_str, std::string new_str)
{
    for (std::string::size_type pos(0); pos != std::string::npos; pos += new_str.length())
    {
        if ((pos = source.find(old_str, pos)) != std::string::npos)
            source.replace(pos, old_str.length(), new_str);
        else
            break;
    }
    return source;
}

std::string thisNode()
{
    char *node = new char[256];
    gethostname(node, 256);
    std::string res = node;
    
    delete node;
    node = nullptr;

    return res;
}

std::string thisService()
{
    std::string document_root = "";
    std::string sapi = sapi_module.name;
    if (sapi == "fpm-fcgi")
    {
        document_root = server_param("DOCUMENT_ROOT");
        document_root = document_root == "" ? "unknown" : document_root;
    }
    else if (sapi == "cli")
    {
        document_root = server_param("SCRIPT_FILENAME");
    }
    document_root = document_root.find_first_of("/") == 0 ? document_root.substr(1) : document_root;
    std::string service = str_replace_all(document_root, "/", "-");
    return service;
}

std::string thisInstance()
{
    int pid = (int)getpid();
    std::string node = thisNode();
    return node + ":" + std::to_string(pid);
}

std::string pushgateway_uri()
{
    std::string node = thisNode();
    std::string service = thisService();
    // std::string instance = thisInstance();
    // std::string uri = "/metrics/job/" + service + "/nodename/" + node + "/instance/" + instance;
    std::string uri = "/metrics/job/php:" + service + "/nodename/" + node;

    return uri;
}